/************************************************************************
 * @file PluginDescriptionManager.cpp
 * @author Ian Chen
 * @version 1.0
 *
 * @section LICENSE
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 * General Public License for more details at
 * http://www.gnu.org/licenses/lgpl.html
 *
 ************************************************************************/
#include <plugin/util/PluginDescriptionManager.h>
#include <plugin/util/PluginDependencyResolver.h>
#include <plugin/util/PluginDescriptionParser.h>

namespace antu
{
namespace plugin
{
PluginDescriptionManager::PluginDescriptionManager()
{
}

PluginDescriptionManager::~PluginDescriptionManager()
{
    this->m_oSortedPluginDescriptions.clear();
    this->m_oResolvedPluginDescriptions.clear();
}

void PluginDescriptionManager::init(const QString& applicationGroup, const QString& path)
{
    this->m_oResolvedPluginDescriptions.clear();
    PluginDependencyResolver::resolve(applicationGroup, path, this->m_oResolvedPluginDescriptions);

    this->m_oSortedPluginDescriptions.clear();
    for(vector<PluginDescriptionSharedPtr>::iterator iter = this->m_oResolvedPluginDescriptions.begin();
        iter != this->m_oResolvedPluginDescriptions.end(); iter++)
    {
        this->m_oSortedPluginDescriptions.insert(pair<QString, PluginDescriptionSharedPtr>(
                                                     (*iter)->code(),
                                                     *iter));
    }
}

PluginDescriptionSharedPtr PluginDescriptionManager::findDescription(const QString &code)
{
    map<QString, PluginDescriptionSharedPtr>::iterator iter =
            this->m_oSortedPluginDescriptions.find(code);

    if (iter == this->m_oSortedPluginDescriptions.end())
        return PluginDescriptionSharedPtr();
    return iter->second;
}

vector<PluginDescriptionSharedPtr> PluginDescriptionManager::findDependentDescriptions(const QString &code)
{
    PluginDescriptionSharedPtr desc = this->findDescription(code);
    vector<PluginDescriptionSharedPtr> result;

    if (desc == nullptr)
        return result;

    int count = desc->requirementCount();
    for (int i = 0; i < count; i++)
    {
        PluginRequirement requirement = desc->requirementAt(i);
        PluginDescriptionSharedPtr dependent = this->findDescription(requirement.code());

        if (dependent != nullptr)
            result.push_back(dependent);
    }

    return result;
}

vector<PluginDescriptionSharedPtr> PluginDescriptionManager::findRequiredDescriptions(const QString &code)
{
    PluginDescriptionSharedPtr desc = this->findDescription(code);
    vector<PluginDescriptionSharedPtr> result;

    if (desc == nullptr)
        return result;

    vector<PluginDescriptionSharedPtr>::const_iterator beginIter = this->m_oResolvedPluginDescriptions.begin();
    vector<PluginDescriptionSharedPtr>::const_iterator endIter = this->m_oResolvedPluginDescriptions.end();

    for (; beginIter != endIter; beginIter++)
    {
        if ((*beginIter)->code() != code)
        {
            if ((*beginIter)->isRequiredPlugin(code))
            {
                result.push_back(*beginIter);
            }
        }
    }

    return result;
}

vector<PluginDescriptionSharedPtr> PluginDescriptionManager::findAllNeededDescriptions(const QStringList &codes)
{
    vector<PluginDescriptionSharedPtr> descriptionsToSearch;
    map<QString, int> listedDescriptionNames;
    vector<QString> itemsToAdd;
    vector<QString> newItemsToAdd;

    itemsToAdd.insert(itemsToAdd.cend(), codes.begin(), codes.end());

    // while there are items to add, do
    while (itemsToAdd.size() > 0)
    {
        vector<QString>::iterator iter = itemsToAdd.begin();
        vector<QString>::iterator end = itemsToAdd.end();

        // add items-to-add to listed items
        for (; iter != end; iter++)
        {
            PluginDescriptionSharedPtr description = this->findDescription((*iter));

            // add each item to be added to be searched
            if (description != nullptr)
            {
                descriptionsToSearch.push_back(description);
                listedDescriptionNames.insert(pair<QString, int>((*iter), 1));
            }
        }

        // check each added item's required items
        // and make sure they are loaded as well
        iter = itemsToAdd.begin();
        for (; iter != end; iter++)
        {
            PluginDescriptionSharedPtr description = this->findDescription((*iter));

            if (description != nullptr)
            {
                int requirementCount = description->requirementCount();

                if (requirementCount > 0)
                {
                    // for each required plugin, check if it's already
                    // listed, if not, put it in the to-be-added-list
                    for (int i = 0; i < requirementCount; i++)
                    {
                        QString requiredCode = description->requirementAt(i).code();

                        if (listedDescriptionNames.find(requiredCode) == listedDescriptionNames.end())
                        {
                            newItemsToAdd.push_back(requiredCode);
                        }
                    }
                }
            }
        }
        // clear itemsToAdd list
        itemsToAdd.clear();
        // copy newItemsToAdd to itemsToAdd
        itemsToAdd.insert(itemsToAdd.cend(), newItemsToAdd.begin(), newItemsToAdd.end());
        // clear newItemsToAdd
        newItemsToAdd.clear();
    }

    // reorder descriptions
    vector<PluginDescriptionSharedPtr> orderedDescriptions;
    PluginDependencyResolver::reorder(descriptionsToSearch, orderedDescriptions);
    return orderedDescriptions;
}

//PluginDescriptionManager PluginDescriptionManager::Manager;

}
}

